#include "map_minedataexchange/MineDataExchange.h"
#include "map_minedataset/MineDataSet.h"
#include "map_helper/FileAccessor.h"
#include "map_helper/StringConverter.h"
#include "map_helper/json/json.h"
#include "map_common/CoorSystem.h"

namespace Mine
{
	CDataHelper::CDataHelper()
	{

	}

	CDataHelper::~CDataHelper()
	{
	
	}

	Mine::CDrillDataSet* CDataHelper::importDrillData(const std::string& in_drillBaseInfo,
		const std::string& in_drillDetailInfo)
	{
		Mine::CDrillDataSet* pDrillDataSet = NULL;
		if (Helper::CFileAccessor::exist(in_drillBaseInfo))
		{
			std::string content = Helper::CFileAccessor::readFile(in_drillBaseInfo);
			std::vector<std::string> rows;
			Helper::CStringConverter::splitString(content, rows, "\n");
			if (rows.size() > 1)
			{
				for (int i = 1; i < rows.size(); ++i)
				{
					std::string row = rows.at(i);
					std::vector<std::string> fields;
					Helper::CStringConverter::splitString(row, fields, ",");
					if (fields.size() > 5)
					{
						if (NULL == pDrillDataSet)
						{
							pDrillDataSet = new Mine::CDrillDataSet();
						}
						
						std::string id = fields[1];
						double x = atof(fields[2].c_str());
						double y = atof(fields[3].c_str());
						double z = atof(fields[4].c_str());
						
						Mine::CDrillFeature* pDrillFeature =
							dynamic_cast<Mine::CDrillFeature*>(pDrillDataSet->createFeature());
						pDrillFeature->setID(id);
						pDrillFeature->setDrillPos(osg::Vec3d(x, y, z));
						pDrillDataSet->add(pDrillFeature);
					}
				}
			}
		}

		if (pDrillDataSet &&
			Helper::CFileAccessor::exist(in_drillDetailInfo))
		{
			std::string content = Helper::CFileAccessor::readFile(in_drillDetailInfo);
			std::vector<std::string> rows;
			Helper::CStringConverter::splitString(content, rows, "\n");
			if (rows.size() > 1)
			{
				std::map<int, Mine::CLithologyInfo*> drillInfos;
				std::string curDrillId = "";
				for (int i = 1; i < rows.size(); ++i)
				{
					std::string row = rows.at(i);
					std::vector<std::string> fields;
					Helper::CStringConverter::splitString(row, fields, ",");
					if (fields.size() > 18)
					{
						std::string drillId = fields[1];
						if (curDrillId == "")
						{
							curDrillId = drillId;
						}
						if (drillId != curDrillId)
						{
							Mine::CDrillFeature* pDrillFeature =
								dynamic_cast<Mine::CDrillFeature*>(pDrillDataSet->get(curDrillId));
							if (pDrillFeature)
							{
								double depth = 0;
								pDrillFeature->getExtendMax() = pDrillFeature->getDrillPos();
								pDrillFeature->getExtendMin() = pDrillFeature->getDrillPos();
								pDrillFeature->getExtendMin().z() -= 100;
								std::map<int, Mine::CLithologyInfo*>::iterator bitr = drillInfos.begin();
								std::map<int, Mine::CLithologyInfo*>::iterator eitr = drillInfos.end();
								while (bitr != eitr)
								{
									depth += bitr->second->Thick;
									bitr->second->Depth = depth;
									pDrillFeature->getLithologyInfo().emplace_back(bitr->second);
									++bitr;
								}
							}
							drillInfos.clear();
							curDrillId = drillId;
						}
						
						int code = atoi(fields[7].c_str());
						double thickValue = atof(fields[8].c_str());
						std::string rockName = fields[12];
						std::string labelLayerName = fields[17];
						std::string imageName = fields[18] + ".png";
						Mine::CLithologyInfo* pInfo = new  Mine::CLithologyInfo();
						pInfo->Code = code;
						pInfo->Thick = thickValue;
						pInfo->RockName = rockName;
						pInfo->LabelLayerName = labelLayerName;
						pInfo->ImageName = imageName;
						pInfo->PureColor = osg::Vec4(rand() % 10 / 10.0, rand() % 10 / 10.0, rand() % 10 / 10.0, 1);
						drillInfos[code] = pInfo;
					}
				}

				if (!drillInfos.empty())
				{
					Mine::CDrillFeature* pDrillFeature =
						dynamic_cast<Mine::CDrillFeature*>(pDrillDataSet->get(curDrillId));
					if (pDrillFeature)
					{
						double depth = 0;
						std::map<int, Mine::CLithologyInfo*>::iterator bitr = drillInfos.begin();
						std::map<int, Mine::CLithologyInfo*>::iterator eitr = drillInfos.end();
						while (bitr != eitr)
						{
							depth += bitr->second->Thick;
							bitr->second->Depth = depth;
							pDrillFeature->getLithologyInfo().emplace_back(bitr->second);
							++bitr;
						}
						if (pDrillFeature->getLithologyInfo().size() == 0)
						{
							int x = 0; 
						}
					}
				}
			}
		}
		return pDrillDataSet;
	}

	Mine::CLaneDataSet* CDataHelper::importLaneData(
		const std::string& in_laneFile)
	{
		Mine::CLaneDataSet* lane_dataset = 0;
		if (Helper::CFileAccessor::exist(in_laneFile))
		{
			std::string content = Helper::CFileAccessor::readFile(in_laneFile);
			std::vector<std::string> rows;
			Helper::CStringConverter::splitString(content, rows, "\n");
			if (rows.size() > 1)
			{
				lane_dataset = new Mine::CLaneDataSet();
				CLaneFeature* pFeature =
					dynamic_cast<CLaneFeature*>(lane_dataset->createFeature());
				osg::Vec3 origin(0, 0, 0);
				std::vector<osg::Vec3> line;
				for (int i = 1; i < rows.size(); ++i)
				{
					std::string row = rows[i];
					std::vector<std::string> elements;
					Helper::CStringConverter::splitString(row, elements, ",");
					if (i == 1 && elements.size() == 3)
					{
						std::string name = elements[0];
						std::string type = elements[1];
						std::string size = elements[2];
						pFeature->setName(name);
						pFeature->setID(name);
						pFeature->setType(type);
						lane_dataset->add(pFeature);
						if (type == "Rect")
						{
							pFeature->setSectionType(Mine::SectionType::ST_RECT);
							elements.clear();
							Helper::CStringConverter::splitString(size, elements, "x");
							if (elements.size() >= 2)
							{
								std::map<std::string, std::string> param = {
									{ "width", elements[0] },
									{ "height", elements[1] }
								};
								pFeature->setSectionParam(param);
							}
						}
						else if (type == "Arc")
						{
							pFeature->setSectionType(Mine::SectionType::ST_ARCHED);
							elements.clear();
							Helper::CStringConverter::splitString(size, elements, "x");
							if (elements.size() >= 3)
							{
								std::map<std::string, std::string> param = {
									{ "width", elements[0] },
									{ "height", elements[1] },
									{ "arc_height", elements[2] }
								};
								pFeature->setSectionParam(param);
							}
						}
					}
					else if (i >= 4 && elements.size() == 4)
					{
						std::string id = elements[0];
						double x = atof(elements[1].c_str());
						double y = atof(elements[2].c_str());
						double z = atof(elements[3].c_str());
						/*if (origin == osg::Vec3(0, 0, 0))
						{
							origin = osg::Vec3(x, y, z);
						}*/
						line.push_back(osg::Vec3(x, y, z) /*- origin*/);
					}
				}
				pFeature->setLinePoints(line);
			}
		}
		return lane_dataset;
	}

	Mine::CLaneDataSet* CDataHelper::importLaneData(
		const std::string& in_laneBaseInfo,
		const std::string& in_laneDetailInfo)
	{
		Mine::CLaneDataSet* lane_dataset = 0;
		if (Helper::CFileAccessor::exist(in_laneBaseInfo))
		{
			std::string content = Helper::CFileAccessor::readFile(in_laneBaseInfo);
			std::vector<std::string> rows;
			Helper::CStringConverter::splitString(content, rows, "\n");
			if (rows.size() > 1)
			{
				for (int i = 1; i < rows.size(); ++i)
				{
					std::string row = rows[i];
					std::vector<std::string> elements;
					Helper::CStringConverter::splitString(row, elements, ",");
					if (elements.size() == 4)
					{
						if (NULL == lane_dataset)
						{
							lane_dataset = new Mine::CLaneDataSet();
						}
						CLaneFeature* pFeature =
							dynamic_cast<CLaneFeature*>(lane_dataset->createFeature());
						
						std::string no = elements[0];
						std::string name = elements[1];
						std::string type = elements[2];
						std::string size = elements[3];
						pFeature->setName(name);
						pFeature->setID(name);
						pFeature->setType(type);
						lane_dataset->add(pFeature);
						if (type == "Rect")
						{
							pFeature->setSectionType(Mine::SectionType::ST_RECT);
							elements.clear();
							Helper::CStringConverter::splitString(size, elements, "x");
							if (elements.size() >= 2)
							{
								std::map<std::string,std::string> param = {
									{ "width", elements[0] },
									{ "height", elements[1] }
								};
								pFeature->setSectionParam(param);
							}
						}
						else if (type == "Arc")
						{
							pFeature->setSectionType(Mine::SectionType::ST_ARCHED);
							elements.clear();
							Helper::CStringConverter::splitString(size, elements, "x");
							if (elements.size() >= 3)
							{
								std::map<std::string, std::string> param = {
									{ "width", elements[0] },
									{ "height", elements[1] },
									{ "arc_height", elements[2] }
								};
								pFeature->setSectionParam(param);
							}
						}						
					}
					else
					{

					}
				}
			}
		}
		if (lane_dataset && Helper::CFileAccessor::exist(in_laneDetailInfo))
		{
			std::string content = Helper::CFileAccessor::readFile(in_laneDetailInfo);
			std::vector<std::string> rows;
			Helper::CStringConverter::splitString(content, rows, "\n");
			if (rows.size() > 1)
			{
				std::string laneName = "";
				osg::Vec3 origin(0, 0, 0);
				CLaneFeature* pFeature = NULL;
				std::vector<osg::Vec3> line;
				for (int i = 1; i < rows.size(); ++i)
				{
					if (i >= 1)
					{
						std::string row = rows[i];
						std::vector<std::string> elements;
						Helper::CStringConverter::splitString(row, elements, ",");
						if (elements.size() == 5)
						{
							std::string name = elements[0];
							if (laneName != "" && laneName != name)
							{
								pFeature = dynamic_cast<CLaneFeature*>(lane_dataset->get(laneName));
								pFeature->setLinePoints(line);
								line.clear();
							}
							laneName = name;
							std::string id = elements[1];
							double x = atof(elements[2].c_str());
							double y = atof(elements[3].c_str());
							double z = atof(elements[4].c_str());
							if (origin == osg::Vec3(0, 0, 0))
							{
								origin = osg::Vec3(x, y, z);
							}
							line.push_back(osg::Vec3(x, y, z) - origin);
						}
						else if (line.size() > 1)
						{
							pFeature = dynamic_cast<CLaneFeature*>(lane_dataset->get(laneName));
							pFeature->setLinePoints(line);
						}

					}
				}
			}
		}
		return lane_dataset;
	}

	bool CDataHelper::computeCoalData(
		Mine::CDrillDataSet* in_drillDataSet,
		std::map<std::string, osg::ref_ptr<osg::Vec3dArray>>& inout_colaDatas,
		const std::string& in_saveDir)
	{
		bool r = false;
		if (in_drillDataSet && in_drillDataSet->size() > 0)
		{
			//std::map<std::string, std::string> layerInfos;
			std::string topID = "";
			std::map<std::string, int> stratumLayerNo;
			for (int i = 0; i < in_drillDataSet->size(); ++i)
			{
				CDrillFeature* pDrill = 
					dynamic_cast<CDrillFeature*>(in_drillDataSet->get(i));
				if (pDrill)
				{
					const std::vector<CLithologyInfo*>& pLithologyInfos = pDrill->getLithologyInfo();
					if (!pLithologyInfos.empty())
					{
						for (int j = 0; j < pLithologyInfos.size(); ++j)
						{
							CLithologyInfo* info = pLithologyInfos[j];
							if (info != NULL
								&& info->LabelLayerName != "*"
								&& info->LabelLayerName != ""
								/*&& info->RockName.find(type.c_str()) >= 0*/)
							{
								std::string layerID /*= Helper::CStringConverter::convert(i+1)*/;
								layerID += info->ImageName;
								layerID += "_";
								layerID += info->LabelLayerName;

								int curLayerNo = (int)stratumLayerNo.size() + 1;
								if (stratumLayerNo.find(layerID) == stratumLayerNo.end())
								{
									stratumLayerNo[layerID] = curLayerNo;
									if (curLayerNo == 1)
										topID = layerID;
								}
								else
								{
									curLayerNo = stratumLayerNo[layerID];
								}

								layerID = Helper::CStringConverter::convert(curLayerNo) + "_" + layerID;

								osg::ref_ptr<osg::Vec3dArray> points = inout_colaDatas[layerID];
								if (points == NULL)
								{
									points = new osg::Vec3dArray();
									inout_colaDatas[layerID] = points;
								}
								osg::Vec3d pos(pDrill->getDrillPos());
								pos.z() -= info->Depth;
								points->push_back(pos);
								
								r = true;
							}

							if (j == pLithologyInfos.size() - 1)
							{
								std::string layerID = Helper::CStringConverter::convert(0);
								layerID += "_" + topID;
								stratumLayerNo[layerID] = j;
								osg::ref_ptr<osg::Vec3dArray> points = inout_colaDatas[layerID];
								if (points == NULL)
								{
									points = new osg::Vec3dArray();
									inout_colaDatas[layerID] = points;
								}
								osg::Vec3d pos(pDrill->getDrillPos());
								points->push_back(pos);
							}
						}
					}
				}
			}
		}

		if (in_saveDir != "")
		{
			std::map<std::string, osg::ref_ptr<osg::Vec3dArray>>::iterator bitr = inout_colaDatas.begin();
			std::map<std::string, osg::ref_ptr<osg::Vec3dArray>>::iterator eitr = inout_colaDatas.end();
			while (bitr != eitr)
			{
				std::string name = bitr->first;
				osg::ref_ptr<osg::Vec3dArray> points = bitr->second;
				Json::Value root; // Json::Value是一种很重要的类型，可以代表任意类型。如int, string, object, array     
				root["name"] = name;
				for (int i = 0; i < points->size(); ++i)
				{
					Json::Value coor;
					coor.append(points->at(i).x());
					coor.append(points->at(i).y());
					coor.append(points->at(i).z());
					root["geometry"].append(coor);
				}
				if (points->size() < 3)
				{
					name += "_empty";
				}
				std::string type = "coal";
				int index = name.find("#");
				if (index >= 0)
				{
					type = "stratum";
				}
				std::string outputPath = in_saveDir + "/" + name + "."+ type;
				std::string outString = root.toStyledString();
				r = Helper::CFileAccessor::writeFile(outString, outputPath);

				++bitr;
			}
			
		}
		return r;
	}

	bool CDataHelper::loadCoalData(const std::string& in_path,
		osg::ref_ptr<osg::Vec3dArray>& inout_points,
		std::string& inout_name)
	{
		osg::ref_ptr<osg::Vec3dArray>& pResult = inout_points;
		if (Helper::CFileAccessor::exist(in_path))
		{
			std::string content = Helper::CFileAccessor::readFile(in_path);
			// 解析json用Json::Reader
			Json::Reader readerinfo(Json::Features::strictMode());
			// Json::Value是一种很重要的类型，可以代表任意类型。如int, string, object, array...
			Json::Value root;
			if (readerinfo.parse(content, root))
			{
				std::string name = root["name"].asString();
				inout_name = name;
				pResult = new osg::Vec3dArray();
				Json::Value geometry = root["geometry"];
				for (int i = 0; i < geometry.size(); ++i)
				{
					Json::Value& coor = geometry[i];
					double x = coor[0].asDouble();
					double y = coor[1].asDouble();
					double z = coor[2].asDouble();
					pResult->push_back(osg::Vec3d(x, y, z));
				}
			}
		}
		if (pResult)
		{
			return true;
		}
		return false;
	}

	bool CDataHelper::computeKTXData(Mine::CDrillDataSet* in_drillDataSet,
		std::map<std::string, std::vector<std::string>>& in_result)
	{
		bool r = false;
		if (in_drillDataSet)
		{
			std::map<std::string, std::vector<std::string>> ktxDatas;
			for (int i = 0; i < in_drillDataSet->size(); ++i)
			{
				Mine::CDrillFeature* pDrill
					= dynamic_cast<Mine::CDrillFeature*>(in_drillDataSet->get(i));
				if (pDrill)
				{
					std::string drillID = pDrill->getID();
					std::vector<std::string> elements;
					Helper::CStringConverter::splitString(drillID,elements,"-");
					if (elements.size() > 1)
					{
						std::string ktxName = elements[0];
						Helper::CStringConverter::replaceString(ktxName, "'", "");
						ktxDatas[ktxName].push_back(drillID);
					}
				}
			}
			if (!ktxDatas.empty())
			{
				in_result = ktxDatas;
				r = true;
			}
		}
		return r;
	}

	Mine::CStratumSectionFeature* CDataHelper::computeStratumSection(
		std::vector<Mine::CDrillFeature*> drills)
	{
		Mine::CStratumSectionFeature* pFeature = NULL;
		if (drills.size() > 1)
		{
			pFeature = new Mine::CStratumSectionFeature();
			std::vector<std::pair<std::string, std::vector<osg::Vec3>>> samplePoints;
			samplePoints.assign(drills[0]->getLithologyInfo().size()+1, std::pair<std::string, std::vector<osg::Vec3>>());
			
			std::vector<std::pair<double, osg::Vec3>> referInfos;
			referInfos.assign(drills[0]->getLithologyInfo().size() + 1, std::pair<double, osg::Vec3>());
			for (int i = 0; i < drills.size(); ++i)
			{
				Mine::CDrillFeature* pDrill = drills.at(i);

				std::pair<std::string, std::vector<osg::Vec3>>& stratumPoints
					= samplePoints[0];
				stratumPoints.first = "";

				if (i == 0)
				{
					referInfos[0] = std::pair<double, osg::Vec3>(0.0, pDrill->getDrillPos());
					stratumPoints.second.push_back(
						osg::Vec3(0, 0, pDrill->getDrillPos().z()));
				}
				else
				{
					double distance = (pDrill->getDrillPos() - referInfos[0].second).length();
					distance += referInfos[0].first;
					referInfos[0] = std::pair<double, osg::Vec3>(distance, pDrill->getDrillPos());
					stratumPoints.second.push_back(
						osg::Vec3(distance, 0, pDrill->getDrillPos().z()));
				}
				
				int lithologySize = pDrill->getLithologyInfo().size();
				for (int j = 0; j < lithologySize; ++j)
				{
					CLithologyInfo* info = pDrill->getLithologyInfo().at(j);
					std::pair<std::string, std::vector<osg::Vec3>>& layerPoints = samplePoints[j + 1];
					layerPoints.first = info->ImageName;
					osg::Vec3d pos(pDrill->getDrillPos());
					pos.z() -= info->Depth;
					if (i == 0)
					{
						referInfos[j + 1] = std::pair<double, osg::Vec3>(0.0, pos);
						layerPoints.second.push_back(
							osg::Vec3(0, 0, pos.z()));
					}
					else
					{
						double distance = (pos - referInfos[j + 1].second).length();
						distance += referInfos[j + 1].first;
						referInfos[j + 1] = std::pair<double, osg::Vec3>(distance, pos);
						layerPoints.second.push_back(
							osg::Vec3(distance, 0, pos.z()));
					}
				}
			}
			double offset = referInfos[drills[0]->getLithologyInfo().size()].first;
			offset /= 2.0;
			for (int i = 0; i < samplePoints.size(); ++i)
			{
				std::pair<std::string, std::vector<osg::Vec3>>& points = samplePoints[i];
				for (int j = 0; j < points.second.size(); ++j)
				{
					points.second[j].x() -= offset;
				}
			}
			pFeature->setPointSamples(samplePoints);
		}
		return pFeature;
	}
}